package cn.iocoder.yudao.framework.redissonlock.core.aop;

import cn.iocoder.yudao.framework.redissonlock.core.keyresolver.RedissonLockKeyResolver;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import cn.iocoder.yudao.framework.redissonlock.core.annotation.RedissonLock;
import org.springframework.context.ApplicationContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 拦截声明了 {@link RedissonLock} 注解的方法，实现分布式锁擦欧总
 *
 * @author 徐煜东
 */
@Aspect
@Slf4j
@RequiredArgsConstructor// Lombok，自动为 final 字段生成构造方法
public class RedissonLockAspect {
    private final RedissonClient redissonClient;
    private final ApplicationContext applicationContext; // 注入 Spring 上下文，用于获取 keyResolver 实例
    // SpEL 表达式解析器
    private final ExpressionParser parser = new SpelExpressionParser();
    // 获取方法参数名的工具
    private final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 切入点，拦截所有带 @RedissonLock 注解的方法。
     */
    @Around("@annotation(redissonLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
        // 根据注解配置解析出锁的 key
        String lockKey = resolveLockKey(joinPoint, redissonLock);
        log.info("【RedissonLockAspect】lockKey = {}", lockKey);
        RLock lock = redissonClient.getLock(lockKey);
        // 阻塞式锁，直到成功或到期
        lock.lock(redissonLock.expire(), redissonLock.timeUnit());
        try {
            return joinPoint.proceed();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 解析出锁的最终 key。
     * 优先用注解里的 key（SpEL 表达式解析），否则用 keyResolver。
     */
    private String resolveLockKey(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) {
        if (!redissonLock.key().isEmpty()) {
            return parseSpEL(joinPoint, redissonLock.key());
        }
        RedissonLockKeyResolver resolver = applicationContext.getBean(redissonLock.keyResolver());
        return resolver.resolve(joinPoint.getArgs(), redissonLock.key());
    }
    /**
     * 解析 SpEL 表达式。
     */
    private String parseSpEL(ProceedingJoinPoint joinPoint, String spelKey) {
        // 1️⃣ 获取当前切入点对应的方法对象
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 2️⃣ 创建 SpEL 的上下文环境，用于存放变量、方法参数等
        EvaluationContext context = new StandardEvaluationContext();
        // 3️⃣ 获取当前方法的参数名列表，例如 ["productId", "message"]
        String[] paramNames = nameDiscoverer.getParameterNames(method);
        // 4️⃣ 获取当前方法的参数值列表，例如 [1024, MessageObj]
        Object[] args = joinPoint.getArgs();
        // 5️⃣ 将参数名和参数值逐一放入 SpEL 上下文
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        // 6️⃣ 使用 SpEL 表达式解析器，解析传入的 spelKey 表达式，最终得到字符串值
        return parser.parseExpression(spelKey).getValue(context, String.class);
    }


}
